package com.example.pm.service.impl;


import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.pm.common.result.R;
import com.example.pm.common.utils.StringUtils;
import com.example.pm.domain.Bo.PageBo;
import com.example.pm.domain.Menu;
import com.example.pm.domain.Model.EmployeeMenu;
import com.example.pm.domain.Role;
import com.example.pm.domain.Vo.PageVo;
import com.example.pm.mapper.EmployeeMenuMapper;
import com.example.pm.mapper.MenuMapper;
import com.example.pm.mapper.RoleMapper;
import com.example.pm.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class MenuServiceImpl implements MenuService {
    List<Integer> updateParentIds = new ArrayList<>();

    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private RoleMapper roleMapper;
    private static final String delete = "DELETE";
    private static final String insert = "INSERT";
    @Autowired
    private EmployeeMenuMapper employeeMenuMapper;

    @Override
    public List<Menu> getAsideMenu() {
        String zh = (String) StpUtil.getLoginId();
        Integer access = roleMapper.getByZh(zh).getAccess();
        List<Menu> menuList = menuMapper.selectList(new LambdaQueryWrapper<>());
        List<Menu> menus = menuList.stream().filter(o -> o.getType() != 2 && o.getAccess() >= access).collect(Collectors.toList());

        LambdaQueryWrapper<EmployeeMenu> lqw = new LambdaQueryWrapper<>();
        lqw.eq(EmployeeMenu::getZh, zh);
        List<EmployeeMenu> employeeMenuList = employeeMenuMapper.selectList(lqw);
        if (!employeeMenuList.isEmpty()) {
            List<Integer> insert = employeeMenuList.stream().filter(o -> o.getRelationship().equals(MenuServiceImpl.insert)).map(EmployeeMenu::getMenuId).collect(Collectors.toList());
            List<Menu> insertMenu = menuList.stream().filter(o -> insert.contains(o.getMenuId())).collect(Collectors.toList());
            menus.addAll(insertMenu);


            List<Integer> delete = employeeMenuList.stream().filter(o -> o.getRelationship().equals(MenuServiceImpl.delete)).map(EmployeeMenu::getMenuId).collect(Collectors.toList());
            List<Menu> deleteMenu = menuList.stream().filter(o -> delete.contains(o.getMenuId())).collect(Collectors.toList());
            menus = menus.stream().filter(o -> !deleteMenu.contains(o)).collect(Collectors.toList());

        }
        List<Menu> menus1 = menus;

        List<Menu> list = menus1.stream().filter(o -> o.getParentId() == 0).collect(Collectors.toList());
        list = list.stream().peek(o ->
                        {
                            List<Menu> childS = menus1.stream().filter(m -> m.getParentId().equals(o.getMenuId())).sorted(Comparator.comparing(Menu::getOrderNum)).collect(Collectors.toList());
                            if (!childS.isEmpty())
                                o.setChildren(childS);
                        }
                )
                .sorted(Comparator.comparing(Menu::getOrderNum))
                .collect(Collectors.toList());
        return list;
    }

    @Override
    public PageVo<Menu> getPage(PageBo pageBo) {
        List<Menu> menuList = menuMapper.selectMenuList();
        List<Menu> menus = menuList.stream().filter(o -> o.getParentId() == 0).sorted(Comparator.comparing(Menu::getOrderNum).thenComparing(Menu::getMenuId)).collect(Collectors.toList());
        List<Menu> menuList1 = menuList.stream().filter(o -> o.getParentId() != 0).collect(Collectors.toList());
        menus.forEach(menu -> {
            List<Menu> list = menuList1.stream().filter(o -> Objects.equals(o.getParentId(), menu.getMenuId())).sorted(Comparator.comparing(Menu::getOrderNum)).collect(Collectors.toList());
            if (!list.isEmpty()) {
                menu.setChildren(list);
            }
        });
        return new PageVo<>(pageBo, menus);
    }

    @Override
    public List<Menu> listParent() {
        return menuMapper.selectMenuList().stream().filter(o -> o.getParentId() == 0).collect(Collectors.toList());
    }

    @Override
    public R<PageVo<Menu>> insert(Menu menu, PageBo pageBo) {
        //目录设置父菜单为空
        if (menu.getType() == 0) {
            menu.setParentId(0);
        }
        LambdaQueryWrapper<Menu> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Menu::getUrl, menu.getUrl());
        boolean exists = menuMapper.exists(lqw);
        int menu_id = menuMapper.selectList(new LambdaQueryWrapper<>()).stream().sorted(Comparator.comparing(Menu::getMenuId).reversed()).collect(Collectors.toList()).get(0).getMenuId() + 1;
        menu.setMenuId(menu_id);
        if (exists) {
            return R.warn("菜单已存在,添加失败");
        } else {
            this.checkMenu(menu);
            menuMapper.insert(menu);
            updateParentIds.add(menu.getParentId());
            this.updateMenuOrderNum();
            PageVo<Menu> list = this.getPage(pageBo);
            return R.ok("添加成功", list);
        }
    }


    /**
     * 更新菜单列表的排序
     */
    @Async
    public void updateMenuOrderNum() {
        updateParentIds.forEach(parentId -> {
            LambdaQueryWrapper<Menu> lqw1 = new LambdaQueryWrapper<>();
            lqw1.eq(Menu::getParentId, parentId);
            List<Menu> menuList = menuMapper.selectList(lqw1);
            List<Menu> menus = menuList.stream().sorted(Comparator.comparing(Menu::getOrderNum).thenComparing(Menu::getMenuId)).collect(Collectors.toList());
            List<Menu> menuChange = new ArrayList<>();
            for (int i = 0; i < menus.size(); i++) {
                Menu menu = menus.get(i);
                if (menu.getOrderNum() != (i + 1)) {
                    Menu menu1 = new Menu();
                    menu1.setMenuId(menu.getMenuId());
                    menu1.setOrderNum(i + 1);
                    menuChange.add(menu1);
                }
            }
            if (StringUtils.isNotEmpty(menuChange)) {
                menuMapper.updateMenuList(menuChange);
            }
        });
        updateParentIds = new ArrayList<>();
    }

    @Override
    public R<PageVo<Menu>> update(Menu menu, PageBo pageBo) {
        if (menu.getType() == 0) menu.setParentId(0);
        LambdaQueryWrapper<Role> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Role::getId, menu.getRoleId());
        Role role = roleMapper.selectOne(lqw);
        Integer access = role.getAccess();
        menu.setAccess(access);
        this.checkMenu(menu);
        menuMapper.updateById(menu);
        updateParentIds.add(menu.getParentId());
        this.updateMenuOrderNum();
        PageVo<Menu> list = this.getPage(pageBo);
        return R.ok("修改成功", list);
    }

    @Override
    public R<PageVo<Menu>> delete(Menu menu, PageBo pageBo) {
        menuMapper.deleteById(menu.getMenuId());
        updateParentIds.add(menu.getParentId());
        this.updateMenuOrderNum();
        PageVo<Menu> list = this.getPage(pageBo);
        return R.ok("删除成功", list);
    }

    @Override
    public PageVo<Menu> getPageBySearch(Menu menu, PageBo pageBo) {
        LambdaQueryWrapper<Menu> lqw = new LambdaQueryWrapper<>();
        if (StringUtils.isNotEmpty(menu.getName()))
            lqw.like(Menu::getName, menu.getName());
        if (StringUtils.isNotEmpty(menu.getUrl()))
            lqw.like(Menu::getUrl, menu.getUrl());
        if (StringUtils.isNotEmpty(menu.getParentId()))
            lqw.eq(Menu::getParentId, menu.getParentId());
        if (StringUtils.isNotEmpty(menu.getType()))
            lqw.eq(Menu::getType, menu.getType());
        List<Menu> menuList = menuMapper.selectList(lqw);
        return new PageVo<>(pageBo, menuList);
    }

    @Override
    public R<List<Integer>> getListByZh(String zh) {
        Integer access = roleMapper.getByZh(zh).getAccess();
        List<Menu> menus = menuMapper.selectList(new LambdaQueryWrapper<>()).stream().filter(o -> o.getType() != 2 && o.getAccess() >= access).collect(Collectors.toList());
        List<Integer> list = menus.stream().map(Menu::getMenuId).collect(Collectors.toList());

        LambdaQueryWrapper<EmployeeMenu> lqw = new LambdaQueryWrapper<>();
        lqw.eq(EmployeeMenu::getZh, zh);
        List<EmployeeMenu> rmList = employeeMenuMapper.selectList(lqw);
        List<Integer> insertList = rmList.stream().filter(o -> "INSERT".equals(o.getRelationship())).collect(Collectors.toList()).stream().map(EmployeeMenu::getMenuId).collect(Collectors.toList());
        List<Integer> deleteList = rmList.stream().filter(o -> "DELETE".equals(o.getRelationship())).collect(Collectors.toList()).stream().map(EmployeeMenu::getMenuId).collect(Collectors.toList());


        // list和deleteList的差集（list - deleteList）
        list = list.stream().filter(item -> !deleteList.contains(item)).collect(Collectors.toList());
        // list和insertList并集（去重）
        list.addAll(insertList);
        list = list.stream().distinct().collect(Collectors.toList());


        return R.ok(list);
    }

    @Override
    public R<List<Menu>> getList() {
        List<Menu> menuList = menuMapper.selectList(new LambdaQueryWrapper<>());
        List<Menu> menus = menuList.stream().filter(o -> o.getParentId() == 0).sorted(Comparator.comparing(Menu::getOrderNum).thenComparing(Menu::getMenuId)).collect(Collectors.toList());
        List<Menu> menuList1 = menuList.stream().filter(o -> o.getParentId() != 0).collect(Collectors.toList());
        menus.forEach(menu -> {
            List<Menu> list = menuList1.stream().filter(o -> Objects.equals(o.getParentId(), menu.getMenuId())).sorted(Comparator.comparing(Menu::getOrderNum)).collect(Collectors.toList());
            if (!list.isEmpty()) {
                menu.setChildren(list);
            }
        });
        return R.ok(menus);
    }

    @Override
    public R<String> changeMenuForEmployee(String zh, List<Integer> newMenu, List<Integer> oldMenu) {
        List<EmployeeMenu> insertList = newMenu.stream().filter(o -> !oldMenu.contains(o)).map(o -> {
            EmployeeMenu employeeMenu = new EmployeeMenu();
            employeeMenu.setZh(zh);
            employeeMenu.setRelationship(insert);
            employeeMenu.setMenuId(o);
            return employeeMenu;
        }).collect(Collectors.toList());
        if (!insertList.isEmpty()) {
            employeeMenuMapper.deleteList(insertList);
            employeeMenuMapper.insertList(insertList);
        }

        List<EmployeeMenu> deleteList = oldMenu.stream().filter(o -> !newMenu.contains(o)).map(o -> {
            EmployeeMenu employeeMenu = new EmployeeMenu();
            employeeMenu.setZh(zh);
            employeeMenu.setRelationship(delete);
            employeeMenu.setMenuId(o);
            return employeeMenu;
        }).collect(Collectors.toList());
        if (!deleteList.isEmpty()) {
            employeeMenuMapper.deleteList(deleteList);
            employeeMenuMapper.insertList(deleteList);
        }


        return R.ok("操作成功");
    }

    //当菜单修改后，检查菜单列表权限的合理性
    public void checkMenu(Menu menu) {
        //当存在子菜单，菜单权限级别不能低于子菜单，压低子菜单权限级别
        LambdaQueryWrapper<Menu> lqw_c = new LambdaQueryWrapper<>();
        lqw_c.eq(Menu::getParentId, menu.getMenuId());
        List<Menu> children = menuMapper.selectList(lqw_c);
        if (children != null && !children.isEmpty()) {
            Integer access = children.stream().max(Comparator.comparing(Menu::getAccess)).get().getAccess();
            if (menu.getAccess() > access) {
                children = children.stream().peek(o -> o.setAccess(menu.getAccess())).collect(Collectors.toList());
                menuMapper.updateMenuList(children);
            }
        }
        //菜单不是最外层的菜单，则搜索菜单的上层菜单,不能比上级菜单级别高
        if (menu.getParentId() != 0) {
            LambdaQueryWrapper<Menu> lqw = new LambdaQueryWrapper<>();
            lqw.eq(Menu::getMenuId, menu.getParentId());
            Menu parentMenu = menuMapper.selectOne(lqw);
            //避免出错，检查是否为空
            if (StringUtils.isNotEmpty(parentMenu)) {
                menu.setAccess(parentMenu.getAccess());
                menuMapper.updateById(menu);
            }
            this.checkMenu(parentMenu);
        }
    }


}
